/*
 * Copyright (C) 2010-2013 ARM Limited. All rights reserved.
 * 
 * This program is free software and is provided to you under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
 * 
 * A copy of the licence is included with the program, and can also be obtained from Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/**
 * @file mali_osk_mali.c
 * Implementation of the OS abstraction layer which is specific for the Mali kernel device driver
 */
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/mali/mali_utgard.h>

#include "mali_osk_mali.h"
#include "mali_kernel_common.h" /* MALI_xxx macros */
#include "mali_osk.h"           /* kernel side OS functions */
#include "mali_uk_types.h"
#include "mali_kernel_linux.h"

_mali_osk_errcode_t _mali_osk_resource_find(u32 addr, _mali_osk_resource_t *res)
{
	int i;

	if (NULL == mali_platform_device)
	{
		/* Not connected to a device */
		return _MALI_OSK_ERR_ITEM_NOT_FOUND;
	}

	for (i = 0; i < mali_platform_device->num_resources; i++)
	{
		if (IORESOURCE_MEM == resource_type(&(mali_platform_device->resource[i])) &&
		    mali_platform_device->resource[i].start == addr)
		{
			if (NULL != res)
			{
				res->base = addr;
				res->description = mali_platform_device->resource[i].name;

				/* Any (optional) IRQ resource belonging to this resource will follow */
				if ((i + 1) < mali_platform_device->num_resources &&
				    IORESOURCE_IRQ == resource_type(&(mali_platform_device->resource[i+1])))
				{
					res->irq = mali_platform_device->resource[i+1].start;
				}
				else
				{
					res->irq = -1;
				}
			}
			return _MALI_OSK_ERR_OK;
		}
	}

	return _MALI_OSK_ERR_ITEM_NOT_FOUND;
}

u32 _mali_osk_resource_base_address(void)
{
	u32 lowest_addr = 0xFFFFFFFF;
	u32 ret = 0;

	if (NULL != mali_platform_device)
	{
		int i;
		for (i = 0; i < mali_platform_device->num_resources; i++)
		{
			if (mali_platform_device->resource[i].flags & IORESOURCE_MEM &&
			    mali_platform_device->resource[i].start < lowest_addr)
			{
				lowest_addr = mali_platform_device->resource[i].start;
				ret = lowest_addr;
			}
		}
	}

	return ret;
}


/* NEXELL_FEATURE_PORTING */
/* nexell add */	
#include <linux/fb.h> 


_mali_osk_errcode_t _mali_osk_device_data_get(struct _mali_osk_device_data *data)
{
	MALI_DEBUG_ASSERT_POINTER(data);

	if (NULL != mali_platform_device)
	{
		struct mali_gpu_device_data* os_data = NULL;

		os_data = (struct mali_gpu_device_data*)mali_platform_device->dev.platform_data;
		if (NULL != os_data)
		{
			/* Copy data from OS dependant struct to Mali neutral struct (identical!) */
			data->dedicated_mem_start = os_data->dedicated_mem_start;
			data->dedicated_mem_size = os_data->dedicated_mem_size;
			data->shared_mem_size = os_data->shared_mem_size;
#if 0 /* org */
			data->fb_start = os_data->fb_start;
			data->fb_size = os_data->fb_size;
#else /* nexell add */	
			{				
				/* NEXELL_FEATURE_PORTING */
				unsigned long temp_fb_start[2] = {0,};
				unsigned long temp_fb_size[2] = {0,};
				unsigned char is_fb_used[2] = {0,};
				data->fb_start = 0;
				data->fb_size = 0;
				int fb_num;
				for(fb_num = 0 ; fb_num < 2 ; fb_num++)
				{
					struct fb_info *info = registered_fb[fb_num];
					if(info)
					{
						if(info->fix.smem_start && info->var.yres_virtual && info->var.yres && info->fix.smem_len)
						{
							is_fb_used[fb_num] = 1;
							temp_fb_start[fb_num] = info->fix.smem_start;
							temp_fb_size[fb_num] = (info->var.yres_virtual/info->var.yres) * info->fix.smem_len;
						}	
					}
				}
				if(is_fb_used[0] && !is_fb_used[1])
				{
					data->fb_start = temp_fb_start[0];
					data->fb_size = temp_fb_size[0];
				}
				else if(!is_fb_used[0] && is_fb_used[1])
				{
					data->fb_start = temp_fb_start[1];
					data->fb_size = temp_fb_size[1];
				}
				else if(is_fb_used[0] && is_fb_used[1])
				{
					if(temp_fb_start[0] < temp_fb_start[1])
					{
						data->fb_start = temp_fb_start[0]; 
						data->fb_size = temp_fb_start[1] - temp_fb_start[0] + temp_fb_size[1];
					}
					else
					{
						data->fb_start = temp_fb_start[1];
						data->fb_size = temp_fb_start[0] - temp_fb_start[1] + temp_fb_size[0];
					}						
				}	
				else
				{
					printk("################ ERROR! There is no available FB ###############\n");
					return _MALI_OSK_ERR_ITEM_NOT_FOUND;
				}	
				
				if(!data->fb_start || !data->fb_size)	
				{
					printk("################ ERROR! wrong FB%d start(0x%x), size(0x%x)###############\n", is_fb_used[0]? 0 : 1, data->fb_start, data->fb_size);
					return _MALI_OSK_ERR_ITEM_NOT_FOUND;
				}	
			}
#endif
			data->utilization_interval = os_data->utilization_interval;
			data->utilization_callback = os_data->utilization_callback;
			data->pmu_switch_delay = os_data->pmu_switch_delay;
			return _MALI_OSK_ERR_OK;
		}
	}

	return _MALI_OSK_ERR_ITEM_NOT_FOUND;
}

mali_bool _mali_osk_shared_interrupts(void)
{
	u32 irqs[128];
	u32 i, j, irq, num_irqs_found = 0;

	MALI_DEBUG_ASSERT_POINTER(mali_platform_device);
	MALI_DEBUG_ASSERT(128 >= mali_platform_device->num_resources);

	for (i = 0; i < mali_platform_device->num_resources; i++)
	{
		if (IORESOURCE_IRQ & mali_platform_device->resource[i].flags)
		{
			irq = mali_platform_device->resource[i].start;

			for (j = 0; j < num_irqs_found; ++j)
			{
				if (irq == irqs[j])
				{
					return MALI_TRUE;
				}
			}

			irqs[num_irqs_found++] = irq;
		}
	}

	return MALI_FALSE;
}
